#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cctype>
#include <vector>
#include <stack>
#include <queue>

using namespace std;

// read_file

char *file_buf;
int file_size;

int read_file(char *file) {
    if (!file) {
        printf("Bad file path.\n");
        return 1;
    }
    FILE *fin;
    if (!(fin = fopen(file, "r"))) {
        printf("Could not open input file.\n");
        return 2;
    }
    if (fseek(fin, 0L, SEEK_END)) {
        printf("fseek failed.\n");
        return 3;
    }
    if ((file_size = ftell(fin)) < 0) {
        printf("ftell failed.\n");
        return 4;
    }
    if (!(file_buf = new char[file_size + 10])) {
        printf("malloc failed.\n");
        return 5;
    }
    if (fseek(fin, 0L, SEEK_SET)) {
        printf("fseek failed.\n");
        return 6;
    }
    file_size = fread(file_buf, sizeof(char), file_size, fin);
    if (ferror(fin)) {
        printf("ferror failed.\n");
        return 7;
    }
    file_buf[file_size] = 0;
    fclose(fin);
    return 0;
}

// copy_article

char *article;
int article_size;

int copy_article() {
    if (!(article = new char[file_size + 10])) {
        printf("malloc failed.\n");
        return 1;
    }
    
    char *dst = article, *src = file_buf, *src_tail = file_buf + file_size;
    while (src < src_tail) {
        while (isspace(*src)) ++src;
        *dst++ = *src++;
        ++article_size;
    }
    
    //article[article_size++] = '#';
    article[article_size] = 0;
    delete file_buf;
    return 0;
}

// parse_expr

#define TOKEN_TYPE_NUM 10

enum token_type_e {
    TK_NULL = 0,
    TK_NUMBER = 1,
    TK_VARIABLE = 2,
    TK_PLUS_MINUS = 3,
    TK_MULTIPLY_DIVIDE = 4,
    TK_POWER = 5,
    TK_UNARY = 6,
    TK_LBRACKET = 7,
    TK_RBRACKET = 8,
    TK_BORDER = 9
};

const char *TOKEN_NAME[TOKEN_TYPE_NUM] = {
    "NUL",
    "NUM",
    "VAR",
    "P&M",
    "M&D",
    "POW",
    "UNA",
    "LBR",
    "RBR",
    "BRD"
};

struct token_t {
    token_type_e type;
    const char *ptr;
    int len;
};

vector<token_t> tokens;

int get_name(const char *buf) {
    const char *cur;
    for (cur = buf; *cur && (isalpha(*cur) || isdigit(*cur) || *cur == '_'); ++cur);
    return cur - buf;
}

int get_num(const char *buf) {
    const char *cur;
    for (cur = buf; *cur && (isdigit(*cur) || *cur == '.'); ++cur);
    return cur - buf;
}

token_t get_token(int offset, int has_lopr) {
    const char *cur = article + offset;
    token_t token;
    token.ptr = cur;
    if (isdigit(*cur) || *cur == '.') {
        int len = get_num(cur);
        token.type = TK_NUMBER;
        token.len = len;
        return token;
    }
    if (isalpha(*cur) || *cur == '_') {
        int len = get_name(cur);
        if (*(cur + len) == '(') {
            token.type = TK_UNARY;
        }
        else {
            token.type = TK_VARIABLE;
        }
        token.len = len;
        return token;
    }
    if (*cur == '+') {
        token.type = TK_PLUS_MINUS;
        token.len = 1;
        return token;
    }
    if (*cur == '-') {
        if (has_lopr) {
            token.type = TK_PLUS_MINUS;
        }
        else {
            token.type = TK_UNARY;
        }
        token.len = 1;
        return token;
    }
    if (*cur == '*' || *cur == '/') {
        token.type = TK_MULTIPLY_DIVIDE;
        token.len = 1;
        return token;
    }
    if (*cur == '^') {
        token.type = TK_POWER;
        token.len = 1;
        return token;
    }
    if (*cur == '(') {
        token.type = TK_LBRACKET;
        token.len = 1;
        return token;
    }
    if (*cur == ')') {
        token.type = TK_RBRACKET;
        token.len = 1;
        return token;
    }
    token.type = TK_NULL;
    token.len = 1;
    return token;
}

void print_token(const token_t &t) {
    printf("%s \"%.*s\"\n", TOKEN_NAME[t.type], t.len, t.ptr);
}

int parse_expr() {
    int cur = 0, has_lopr = 0;
    while (cur < article_size) {
        token_t t = get_token(cur, has_lopr);
        //printf("TOKEN %s #%.*s#\n", TOKEN_NAME[t.type], t.len, t.ptr);
        if (t.type == TK_NULL) {
            printf("Bad token at %d: ", cur);
            print_token(t);
            printf("\n");
            printf("%s", article + cur);
            return 1;
        }
        tokens.push_back(t);
        cur += t.len;
        has_lopr = (t.type == TK_NUMBER || t.type == TK_VARIABLE || t.type == TK_RBRACKET);
    }
    
    token_t border;
    border.type = TK_BORDER;
    border.ptr = "#";
    border.len = 1;
    tokens.push_back(border);
    return 0;
}

// print_pretty

int get_sublen(int now) {
    int pair = 0;
    for (int i = now, end = tokens.size(); i < end; ++i) {
        const token_t &cur = tokens[i];
        if (cur.type == TK_LBRACKET) ++pair;
        if (cur.type == TK_RBRACKET) --pair;
        if (!pair) return i - now + 1;
    }
    return -1;
}

void print_indent(int indent) {
    for (int i = 0; i < indent; ++i) {
        printf("    ");
    }
}

void print_token_str(const token_t &t) {
    if (t.type == TK_PLUS_MINUS) 
    {
        printf(" %.*s ", t.len, t.ptr);
    }
    if (t.type == TK_NUMBER || 
        t.type == TK_VARIABLE ||
        t.type == TK_POWER || 
        t.type == TK_UNARY ||
        t.type == TK_LBRACKET ||
        t.type == TK_RBRACKET || 
        t.type == TK_MULTIPLY_DIVIDE) 
    {
        printf("%.*s", t.len, t.ptr);
    }
}

int print_pretty() {
    int indent = 0, line_start = 1;
    for (int i = 0, end = tokens.size(); i < end; ++i) {
        const token_t &cur = tokens[i];
        if (line_start) {
            line_start = 0;
            print_indent(indent);
        }
        if (cur.type == TK_LBRACKET) {
            int len = get_sublen(i);
            if (len < 0) {
                printf("Missing parentheses!\n");
                return 1;
            }
            if (len <= 15) {
                for (int j = i; j < i + len; ++j)
                    print_token_str(tokens[j]);
                i += len - 1;
                //printf("#");
                continue;
            }
            else {
                printf("(\n");
                ++indent;
                line_start = 1;
                //printf("#");
                continue;
            }
        }
        if (cur.type == TK_RBRACKET) {
            --indent;
            printf("\n");
            print_indent(indent);
            printf(")");
            //printf("#");
            continue;
        }
        print_token_str(cur);
        //printf("#");
    }
    printf("\n");
    return 0;
}

// gen_oprtable

const int OPR_ORDER[] = {
    TK_BORDER,
    TK_RBRACKET,
    TK_PLUS_MINUS,
    TK_MULTIPLY_DIVIDE,
    TK_POWER,
    TK_UNARY,
    TK_LBRACKET,
};

int OPR_ORDER_INV[TOKEN_TYPE_NUM];

int OPR_PRE[TOKEN_TYPE_NUM][TOKEN_TYPE_NUM];

int precede(const token_t &a, const token_t &b) {
    return OPR_PRE[a.type][b.type];
}

int gen_oprtable() {
    for (int i = 0; i < TOKEN_TYPE_NUM; ++i) {
        for (int j = 0; j < TOKEN_TYPE_NUM; ++j) {
            OPR_PRE[i][j] = -3;
        }
    }
    for (int i = 0, i_ = sizeof(OPR_ORDER) / sizeof(int); i < i_; ++i) {
        OPR_ORDER_INV[OPR_ORDER[i]] = i + 1;
    }
    for (int i = 0, i_ = sizeof(OPR_ORDER) / sizeof(int); i < i_; ++i) {
        for (int j = 0; j < i_; ++j) {
            int o1 = OPR_ORDER[i], o2 = OPR_ORDER[j], pre = -3;
            if (i == j) pre = -1; 
            if (OPR_ORDER_INV[o1] > OPR_ORDER_INV[o2]) pre = -2;
            if (OPR_ORDER_INV[o1] < OPR_ORDER_INV[o2]) pre = 1;
            if (o1 == TK_LBRACKET && OPR_ORDER_INV[o2] > OPR_ORDER_INV[TK_RBRACKET]) pre = 1;
            if (o1 == TK_BORDER && o2 == TK_BORDER) pre = 0;
            if (o1 == TK_LBRACKET && o2 == TK_RBRACKET) pre = 0;
            OPR_PRE[o1][o2] = pre;
        }
    }

    /*
    for (int i = 0; i < TOKEN_TYPE_NUM; ++i) {
        for (int j = 0; j < TOKEN_TYPE_NUM; ++j) {
            printf("OPR_PRE[%s][%s] = %d\n", TOKEN_NAME[i], TOKEN_NAME[j], OPR_PRE[i][j]);
        }
    }
    //*/
    return 0;
}

// build_tree

enum node_type_e {
    NODE_NULL = 0,
    NODE_UNARY = 1,
    NODE_BINARY = 2,
    NODE_VALUE = 3
};

const char *NODE_TYPE_NAME[] = {
    "NUL",
    "UNA",
    "BIN",
    "VAL"
};

struct node_t {
    node_t *parent, *left, *right;
    node_type_e type;
    token_t token;
    int tag;
    int constant;
};

stack<node_t *> operators, operands;

node_t *root = nullptr;

int is_opr(const token_t &t) {
    return t.type == TK_PLUS_MINUS || 
           t.type == TK_MULTIPLY_DIVIDE || 
           t.type == TK_POWER || 
           t.type == TK_UNARY ||
           t.type == TK_LBRACKET ||
           t.type == TK_RBRACKET ||
           t.type == TK_BORDER;
}

int is_val(const token_t &t) {
    return t.type == TK_NUMBER || t.type == TK_VARIABLE;
}

int is_unary(const token_t &t) {
    return t.type == TK_UNARY;
}

int is_binary(const token_t &t) {
    return t.type == TK_PLUS_MINUS || 
           t.type == TK_MULTIPLY_DIVIDE || 
           t.type == TK_POWER;
}

void _print_tree(const node_t *root, int depth) {
    if (!root) return;
    print_indent(depth);
    printf("%s #%d ", root->constant ? "C" : "V", root->tag);
    print_token(root->token);
    _print_tree(root->left, depth + 1);
    _print_tree(root->right, depth + 1);
}

void print_tree(const node_t *root) {
    _print_tree(root, 0);
}

node_t *create_node(node_type_e type) {
    node_t *node = new node_t;
    node->left = nullptr;
    node->right = nullptr;
    node->parent = nullptr;
    node->type = type;
    node->tag = -1;
    node->constant = 0;
    return node;
}

int build_tree() {
    node_t *border = create_node(NODE_NULL);
    border->token.type = TK_BORDER;
    border->token.ptr = "#";
    border->token.len = 1;
    operators.push(border);
    
    for (auto iter = tokens.begin(); iter != tokens.end(); ) {
        const token_t &cur = *iter;
        const token_t &top = (operators.top())->token;
        if (is_val(cur)) {
            node_t *node = create_node(NODE_VALUE);
            node->token = cur;
            node->constant = cur.type == TK_NUMBER;
            operands.push(node);
            ++iter;
            continue;
        }
        if (is_opr(cur)) {
            int pre = precede(top, cur);
            if (pre == 1) {
                node_t *node = create_node(NODE_NULL);
                if (is_unary(cur)) node->type = NODE_UNARY;
                if (is_binary(cur)) node->type = NODE_BINARY;
                node->token = cur;
                operators.push(node);
                ++iter;
                continue;
            }
            if (pre == 0) {
                node_t *o_top = operators.top();
                if (top.type == TK_BORDER && cur.type == TK_BORDER) {
                    operators.pop();
                    delete o_top;
                    break;
                }
                if (top.type == TK_LBRACKET && cur.type == TK_RBRACKET) {
                    operators.pop();
                    delete o_top;
                    ++iter;
                    continue;
                }
                printf("Illegal operator at %lld: ", cur.ptr - article);
                print_token(cur);
                printf("\n");
                printf("Following %lld: ", top.ptr - article);
                print_token(top);
                printf("\n");
                return 1;
            }
            if (pre == -1 || pre == -2) {
                int o_num = is_unary(top) ? 1 : is_binary(top) ? 2 : 0;
                if (operands.size() < (size_t) o_num) {
                    printf("Missing operands at %lld: ", cur.ptr - article);
                    print_token(cur);
                    printf("\n");
                    printf("Following %lld: ", top.ptr - article);
                    print_token(top);
                    printf("\n");
                    return 2;
                }
                node_t *o_top = operators.top();
                node_t *v_top = operands.top();
                if (is_unary(top)) {
                    o_top->left = v_top;
                    v_top->parent = o_top;
                    o_top->constant = v_top->constant;
                    operators.pop();
                    operands.pop();
                    operands.push(o_top);
                }
                if (is_binary(top)) {
                    o_top->right = v_top;
                    v_top->parent = o_top;
                    o_top->constant = v_top->constant;
                    operands.pop();
                    v_top = operands.top();
                    o_top->left = v_top;
                    v_top->parent = o_top;
                    o_top->constant &= v_top->constant;
                    operands.pop();
                    operators.pop();
                    operands.push(o_top);
                }
                continue;
            }
            printf("Illegal operator at %lld: ", cur.ptr - article);
            print_token(cur);
            printf("\n");
            printf("Following %lld: ", top.ptr - article);
            print_token(top);
            printf("\n");
            return 3;
        }
    }
    
    if (!operators.empty()) {
        printf("Unfinished expression.\n");
        return 4;
    }
    if (operands.size() > 1) {
        printf("Unexpected value in expression.\n");
        return 5;
    }
    
    root = operands.top();
    // print_tree(root);
    return 0;
}

// refactor_expr

struct tag_t {
    const node_t *node;
    int id, ref_num;
};

vector<tag_t> tags;
vector<int> tag_visited;

tag_t create_tag(int id, const node_t *root) {
    tag_t tag;
    tag.node = root;
    tag.id = id;
    tag.ref_num = 1;
    return tag;
}

int compare_token(const token_t &a, const token_t &b) {
    return (a.type == b.type) && (a.len == b.len) && (!strncmp(a.ptr, b.ptr, a.len));
}

int compare_node(const node_t *a, const node_t *b) {
    if (!a || !b) return 0;
    if (a == b) return 1;
    if (a->tag == b->tag) return 1;
    if (a->type != b->type) return 0;
    if (!compare_token(a->token, b->token)) return 0;
    if (a->type == NODE_VALUE && b->type == NODE_VALUE) return 1;
    if (a->type == NODE_UNARY) {
        return compare_node(a->left, b->left);
    }
    if (a->type == NODE_BINARY) {
        return compare_node(a->left, b->left) && compare_node(a->right, b->right);
    }
    return 0;
}

int find_tag(const node_t *root) {
    for (int i = 0; i < (int) tags.size(); ++i) {
        tag_t &tag = tags[i];
        if (compare_node(root, tag.node)) {
            return tag.id;
        }
    }
    return -1;
}

void tag_node(node_t *root) {
    if (!root) return;
    tag_node(root->left);
    tag_node(root->right);
    int tag = find_tag(root);
    if (tag < 0) {
        tag = tags.size();
        tags.push_back(create_tag(tag, root));
        tag_visited.push_back(0);
    }
    else {
        ++(tags[tag].ref_num);
    }
    root->tag = tag;
}

void print_sub_str(const node_t *root) {
    printf("sub_%d", root->tag);
}

void print_expr_sub(const node_t *root, int ref_parent) {
    if (root->type == NODE_VALUE) {
        print_token_str(root->token);
        return;
    }
    int ref = tags[root->tag].ref_num;
    if (ref > ref_parent && !(root->constant)) {
        //printf("@%d,%d:", ref, ref_parent);
        print_sub_str(root);
    }
    else {
        if (root->type == NODE_UNARY) {
            print_token_str(root->token);
            printf("(");
            print_expr_sub(root->left, ref);
            printf(")");
        }
        if (root->type == NODE_BINARY) {
            int bracket = 0;
            if (root->parent) {
                int pre = precede(root->parent->token, root->token);
                if (root->parent->type != NODE_UNARY && pre == -2) bracket = 1;
            }
            if (bracket) printf("(");
            print_expr_sub(root->left, ref);
            print_token_str(root->token);
            print_expr_sub(root->right, ref);
            if (bracket) printf(")");
        }
    }
}

void print_sub_value(const node_t *root) {
    int ref = tags[root->tag].ref_num;
    printf("float ");
    print_sub_str(root);
    printf(" = ");
    if (root->type == NODE_UNARY) {
        print_token_str(root->token);
        printf("(");
        print_expr_sub(root->left, ref);
        printf(")");
    }
    if (root->type == NODE_BINARY) {
        print_expr_sub(root->left, ref);
        print_token_str(root->token);
        print_expr_sub(root->right, ref);
    }
    printf(";\n");
}

void print_refactor_expr(const node_t *root, int ref_parent) {
    if (!root) return;
    int ref = tags[root->tag].ref_num;
    print_refactor_expr(root->left, ref);
    print_refactor_expr(root->right, ref);
    if (tag_visited[root->tag]) return;
    tag_visited[root->tag] = 1;
    if (root->type == NODE_VALUE) return;
    if (ref <= ref_parent || root->constant) return;
    // printf("%s #ref=%d,%d ", root->constant ? "C" : "V", ref, ref_parent);
    print_sub_value(root);
}

int refactor_expr() {
    tag_node(root);
    //print_tree(root);

    print_refactor_expr(root, 1);
    print_sub_value(root);

    return 0;
}

// main

int main(int argc, char* argv[]) {
    if (argc < 2) {
        printf("No file specified.\n");
        return 1;
    }
    int ret;
    if ((ret = read_file(argv[1]))) {
        return ret + 100;
    }
    if ((ret = copy_article())) {
        return ret + 200;
    }
    if ((ret = parse_expr())) {
        return ret + 300;
    }
    
    /*
    if ((ret = print_pretty())) {
        return ret + 400;
    }
    //*/
    
    if ((ret = gen_oprtable())) {
        return ret + 500;
    }
    if ((ret = build_tree())) {
        return ret + 600;
    }
    if ((ret = refactor_expr())) {
        return ret + 700;
    }
    
    return 0;
}